#include <string.h>
#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

//结论
//1、函数模板可以与普通函数同时存在（重载）
//2、普通函数优先于函数模板执行
//3、函数模板可以与函数模板进行重载
//
//
//参数T进行推导的过程都发生在编译的时候
//
//在编译的时候，将参数T进行推导
//
//
//         推导参数
//函数模板---------模板函数
//       将参数T进行了实例化 
//T int    double  char
//实例化：隐式实例化与显示实例化
//
//
//
/* template <class T>//此处可以使用typename，也可以使用class */
template <typename T>//此处可以使用typename，也可以使用class
T add(T x, T y)
{
    cout << "T add(T, T)" << endl;
    return x + y;
}

//模板的特化
//分类：全特化和偏特化（部分特化）
//全特化：将模板参数全部以特殊版本的形式实例化
//偏特化（部分特化）：将模板参数中的部分实例化出来
template <>
const char *add<const char *>(const char *px, const char *py)
{
    cout << "const char *add(const char *, const char *)" << endl;
    //考虑一下px与py是不是空指针
    char *tmp = new char[strlen(px) + strlen(py) + 1]();
    strcpy(tmp, px);
    strcat(tmp, py);

    return tmp;
}

#if 0
template <typename T, typename T2>//此处可以使用typename，也可以使用class
T add(T x, T2 y)
{
    cout << "T add(T, T)" << endl;
    return x + y;
}
#endif

template <typename T>//此处可以使用typename，也可以使用class
T add(T x, T y, T z)
{
    cout << "T add(T, T, T)" << endl;
    return x + y + z;
}

#if 0
int add(int x, int y)
{
    cout << "int add(int, int)" << endl;
    return x + y;
}
#endif

void test()
{
    int ia = 3, ib = 4, ic = 5;
    char ca = 'a', cb = 1;
    double da = 3.3, db = 4.4;
    string s1("hello"),s2("world");

    const char *pstr1 = "hello";
    const char *pstr2 = "world";

    cout << "add(ia, ib) = " << add(ia, ib) << endl;//隐式实例化
    cout << "add(ca, cb) = " << add<char>(ca, cb) << endl;//显示实例化
    cout << "add(da, db) = " << add<double>(da, db) << endl;
    cout << "add(s1, s2) = " << add(s1, s2) << endl;
    cout << "add(ia, ib, ic) = " << add(ia, ib, ic) << endl;

    //使用模板进行推导的时候，因为ia是int类型，所以会将
    //T推导为int，但是db是double类型，会将T推导为double
    //此时会产生二义性
    /* cout << "add(ia, db) = " << add(ia, db) << endl; */

    cout << "add(pstr1, pstr2) = " << add(pstr1, pstr2) << endl;
}

int main(int argc, char *argv[])
{
    test();
    return 0;
}

